package com.lagou.filter;

import com.lagou.bean.TakeTime;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {

    private static Logger log = LoggerFactory.getLogger(TPMonitorFilter.class);

    Map<String, List<TakeTime>> map = new ConcurrentHashMap<>();

    public TPMonitorFilter() {

        // 每隔5s打印一次最近1分钟内每个方法的TP90、TP99的耗时情况
        new Thread(() -> {
            while (true) {

                // 获取每个方法的耗时记录
                for (Map.Entry<String, List<TakeTime>> entry : map.entrySet()) {
                    String methodName = entry.getKey();
                    List<TakeTime> timeList = entry.getValue();

                    long tp90 = tp(timeList, 90);
                    long tp99 = tp(timeList, 99);

                    log.info("method:" + methodName + "最近1分钟内" + "===>TP90:" + tp90 + ",===>TP99:" + tp99);
                }

                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long start = System.currentTimeMillis();
        Result result = invoker.invoke(invocation);
        long end = System.currentTimeMillis();

        // 调用耗时耗时
        long useTime = end - start;
        TakeTime takeTime = new TakeTime(start, useTime);

        // 对各个方法的耗时进行记录
        String methodName = invocation.getMethodName();
        if (!map.containsKey(methodName)) {
            List<TakeTime> taskList = new CopyOnWriteArrayList<>();
            taskList.add(takeTime);
            map.put(methodName, taskList);
        } else {
            List<TakeTime> timeList = map.get(methodName);
            timeList.add(takeTime);
        }

        return result;
    }

    // 计算一分钟内TP
    private long tp(List<TakeTime> list, int percent) {
        long now = System.currentTimeMillis();

        for (TakeTime takeTime: list) {
            if (now > takeTime.getSysTime() + 60000 ) {
                list.remove(takeTime);
            }
        }

        List<TakeTime> temp = new ArrayList<>();
        temp.addAll(list);

        List<Long> timeList = temp.stream().map(t -> t.getUseTime()).collect(Collectors.toList());

        int index = timeList.size() * percent / 100;
        Collections.sort(timeList);
        long value = timeList.get(index).longValue();
        return value;

    }
}
